package com.godme.rocket.controller;


import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.godme.rocket.utils.RateLimitHelper;
import com.godme.rocket.repo.GoodsRepository;
import com.godme.rocket.repo.OrderRepository;
import com.godme.rocket.utils.SequenceGenerator;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.common.message.Message;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.PostConstruct;
import java.util.Collections;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Supplier;

@RestController
@RequestMapping("/shopping")
public class ShoppingController {

    @Autowired
    GoodsRepository goodsRepository;

    @Autowired
    OrderRepository orderRepository;

    final AtomicLong buySequence = new AtomicLong(0);
    final AtomicInteger paySequence = new AtomicInteger(0);
    final String resourceName = "shopping";

    @Autowired
    DefaultMQProducer producer;

    @Value("${rate.limit}")
    Integer rateLimit;


    @Value("${delay.level}")
    Integer delayLevel;

    @PostConstruct
    public void initRateLimit(){
        FlowRule flowRule = new FlowRule();
        flowRule.setCount(rateLimit);
        flowRule.setResource(resourceName);
        FlowRuleManager.loadRules(Collections.singletonList(flowRule));
    }


    public void sendMessage(String msg) throws Exception {
        Message message = new Message("shopping", msg.getBytes());
        message.setDelayTimeLevel(delayLevel);
        producer.send(message);

    }

    final Supplier<String> failureSupplier = ()->{
        System.err.println(buySequence.incrementAndGet() + "秒杀失败");
        return "failure";
    };

    final Supplier<String> successSupplier = ()->{
        try{
            if(GoodsRepository.sellOut.get()){
                System.err.println(buySequence.incrementAndGet()+ " sellOut");
                return "sellOut";
            }
            goodsRepository.decreaseProduct();
            String orderId = SequenceGenerator.generateSequence();
            orderRepository.insertOrder(orderId);
            sendMessage(orderId);
            String residue = goodsRepository.residue();
            if(residue.equals("0")){
                GoodsRepository.sellOut.set(true);
            }
            System.err.println(buySequence.incrementAndGet() + " 秒杀成功，剩余库存：" + residue);
            return orderId;
        }catch (Exception e){
            return failureSupplier.get();
        }
    };


    @GetMapping("/buy")
    public String buy(){
        return RateLimitHelper.apply("shopping", GoodsRepository.sellOut.get(), successSupplier, failureSupplier);
    }


    @GetMapping("/pay/{orderId}")
    public void pay(@PathVariable("orderId") String orderId){
        orderRepository.updateOrder(orderId, true);
        System.err.println(paySequence.incrementAndGet() + "付款成功");
    }

    @GetMapping("/reset/{count}")
    public void reset(@PathVariable(required = false, name = "count") int count){
        goodsRepository.initCount(count);
        buySequence.set(0);
        paySequence.set(0);
    }

    @GetMapping("/residue")
    public String residue(){
        return goodsRepository.residue();
    }
}
